package com.luo.chengrui.module.crbpm.service.definition;

import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.luo.chengrui.module.crbpm.controller.admin.definition.vo.modelactivity.ModelActivityCreateReqVO;
import com.luo.chengrui.module.crbpm.controller.admin.definition.vo.modelactivity.ModelActivityPageReqVO;
import com.luo.chengrui.module.crbpm.convert.definition.ModelActivityConvert;
import com.luo.chengrui.module.crbpm.dal.dataobject.definition.ModelActivityDO;
import com.luo.chengrui.module.crbpm.dal.mysql.definition.ModelActivityMapper;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.*;
import org.flowable.engine.repository.Model;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.luo.chengrui.module.crbpm.enums.ErrorCodeConstants.MODEL_ACTIVITY_NOT_EXISTS;

/**
 * 流程模型正式版环节 Service 实现类
 *
 * @author luodz
 */
@Service
@Validated
@Slf4j
public class ModelActivityServiceImpl implements ModelActivityService {

    @Resource
    private ModelActivityMapper modelActivityMapper;

    @Resource
    @Lazy
    private BpmModelService modelService;

    @Override
    public String createModelActivity(ModelActivityCreateReqVO createReqVO) {
        // 插入
        ModelActivityDO modelActivity = ModelActivityConvert.INSTANCE.convert(createReqVO);
        modelActivityMapper.insert(modelActivity);
        // 返回
        return modelActivity.getId();
    }

    @Override
    public void updateModelActivity(Model model) {
        BpmnModel bpmModel = modelService.getBpmnModel(model.getId());
        StartEvent startElement = null;
        for (org.flowable.bpmn.model.Process process : bpmModel.getProcesses()) {
            Collection<FlowElement> flowElements = process.getFlowElements();
            if (!CollectionUtils.isEmpty(flowElements)) {
                for (FlowElement flowElement : flowElements) {
                    StringBuilder sb = new StringBuilder();
                    sb.append("id=").append(flowElement.getId()).append(",name=").append(flowElement.getName()).append(",type=").append(flowElement.getClass().getSimpleName());
                    log.debug(sb.toString());
                    if (flowElement instanceof StartEvent) {
                        startElement = (StartEvent) flowElement;
                    }
                }
            }
        }

        List<ModelActivityCreateReqVO> list = new ArrayList<>();
        List<ModelActivityDO> listDo = new ArrayList<>();
        ModelActivityCreateReqVO start = new ModelActivityCreateReqVO();
        start.setActivityId(startElement.getId());
        start.setActivityName(startElement.getName());
        start.setActivityType(startElement.getClass().getSimpleName());
        start.setFullActivityId(FULL_PATH_SEPARATOR + startElement.getId());
        start.setFullActivityName(FULL_PATH_SEPARATOR + "Start");
        start.setSort(0);
        list.add(start);
        getOutgoingFlows(list, startElement, start, 1);
        for (ModelActivityCreateReqVO vo : list) {
            vo.setModelId(model.getId());
            vo.setModelKey(model.getKey());
            vo.setStatus(CommonStatusEnum.ENABLE.getStatus());
            listDo.add(ModelActivityConvert.INSTANCE.convert(vo));
        }
        modelActivityMapper.physicalDeletion(model.getId());
        modelActivityMapper.insertBatch(listDo);
    }

    /**
     * 递归解析后续所有节点，解析存储在list对象中。
     *
     * @param list     所有环节列表，新增到数据库的数据
     * @param flowNode 待解析后续节点的环节
     * @param entity   当前环节Insert对象
     * @param sort     后续节点序号
     * @return
     */
    private List<ModelActivityCreateReqVO> getOutgoingFlows(List<ModelActivityCreateReqVO> list, FlowElement flowNode, ModelActivityCreateReqVO entity, int sort) {
        if (flowNode instanceof EndEvent) {
            return list;
        }
        List<SequenceFlow> s = ((FlowNode) flowNode).getOutgoingFlows();
        if (s != null && s.size() > 0) {
            for (int i = 0; i < s.size(); i++) {
                // 连接线
                SequenceFlow flow = s.get(i);
                ModelActivityCreateReqVO flowActivity = new ModelActivityCreateReqVO();
                flowActivity.setActivityId(flow.getId());
                flowActivity.setActivityName(flow.getName());
                flowActivity.setConditionPattern(flow.getConditionExpression());
                flowActivity.setActivityType(flow.getClass().getSimpleName());
                flowActivity.setParentActivityId(flowNode.getId());
                flowActivity.setFullActivityId(entity.getFullActivityId() + FULL_PATH_SEPARATOR + flowActivity.getActivityId());
                flowActivity.setFullActivityName(entity.getFullActivityName());
                flowActivity.setSort(sort);
                // 目标环节
                ModelActivityCreateReqVO targetElement = new ModelActivityCreateReqVO();
                targetElement.setActivityId(flow.getTargetFlowElement().getId());
                targetElement.setActivityName(flow.getTargetFlowElement().getName());
                targetElement.setActivityType(flow.getTargetFlowElement().getClass().getSimpleName());
                targetElement.setParentActivityId(flow.getId());
                targetElement.setSort(sort + 1);
                targetElement.setFullActivityId(flowActivity.getFullActivityId() + FULL_PATH_SEPARATOR + targetElement.getActivityId());
                targetElement.setFullActivityName(flowActivity.getFullActivityName());
                if (flow.getTargetFlowElement() instanceof EndEvent) {
                    targetElement.setFullActivityName(flowActivity.getFullActivityName() + FULL_PATH_SEPARATOR + "End");
                } else {
                    if (flow.getTargetFlowElement() instanceof UserTask) {
                        String activityName = targetElement.getActivityName();
                        if ((StrUtil.isEmpty(activityName))) {
                            activityName = flow.getTargetFlowElement().getClass().getSimpleName() + targetElement.getSort();
                        }
                        targetElement.setFullActivityName(flowActivity.getFullActivityName() + FULL_PATH_SEPARATOR + activityName);
                    }
                }
                // 如果目标环节已经在路径中已经存在，则说明存在循环，则线和环节都不再添加；
                if (!entity.getFullActivityId().contains(targetElement.getActivityId())) {
                    list.add(flowActivity);
                    list.add(targetElement);
                    getOutgoingFlows(list, flow.getTargetFlowElement(), targetElement, sort + 2);
                }
            }
        }
        return list;
    }

    @Override
    public void deleteModelActivity(String id) {
        // 校验存在
        validateModelActivityExists(id);
        // 删除
        modelActivityMapper.deleteById(id);
    }

    private void validateModelActivityExists(String id) {
        if (modelActivityMapper.selectById(id) == null) {
            throw exception(MODEL_ACTIVITY_NOT_EXISTS);
        }
    }

    private void validateModelActivityExists(String id, SFunction<ModelActivityDO, ?> field, Object value, final ErrorCode errorCode) {
        ModelActivityDO entity = modelActivityMapper.selectOne(field, value);
        if (entity == null) {
            return;
        }
        if (id == null || !Objects.equals(entity.getId(), id)) {
            throw exception(errorCode);
        }
    }

    @Override
    public ModelActivityDO getModelActivity(String id) {
        return modelActivityMapper.selectById(id);
    }

    @Override
    public List<ModelActivityDO> getModelActivityList(Collection<String> ids) {
        return modelActivityMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<ModelActivityDO> getModelActivityPage(ModelActivityPageReqVO pageReqVO) {
        return modelActivityMapper.selectPage(pageReqVO);
    }

}
